﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;
using System.Xml;
using VSIXUtilities.Helpers.Extensions;
using System.IO;

namespace CodeDocumentationSynchronizer.CodDocSync.Helpers
{
    class SynchronizationCore
    {
        FileCodeModel FileCodeModelInstance;
        string Extension;

        public SynchronizationCore(ProjectItem ProjectItemInstance)
        {
            this.FileCodeModelInstance = ProjectItemInstance.FileCodeModel;
            Extension = Path.GetExtension(ProjectItemInstance.Name);
        }

        public bool AddRemarks
        {
            get;
            set;
        }

        public void Dump()
        {
            foreach (CodeElement CodeElementInstance in FileCodeModelInstance.CodeElements)
            {
                Dump(CodeElementInstance);
            }
        }

        private void Dump(CodeElement CodeElementInstance)
        {
            if (CodeElementInstance.CanBeDocCommented())
            {
                SyncElement(CodeElementInstance);
            }
            if (CodeElementInstance.CanHaveDoumentableChildren())
            {
                foreach (CodeElement CodeElementChild in CodeElementInstance.Children)
                {
                    Dump(CodeElementChild);
                }
            }
        }
        private void SyncElement(CodeElement CodeElementInstance)
        {
            bool WereParametersDumped = !CodeElementInstance.IsParametersProvider();
            bool WereTypeParametersDumped = !CodeElementInstance.HasGenericArguments();
            XmlDocument XmlDocumentDocComment = CodeElementInstance.GetDocComment(Extension);
            StringBuilder StringBuilderDocComment = new StringBuilder();

            XmlNode XmlNodeSummary = XmlDocumentDocComment.DocumentElement.SelectSingleNode("summary");
            if(XmlNodeSummary == null || string.IsNullOrEmpty(XmlNodeSummary.InnerText.Trim()))
            {
                StringBuilderDocComment.AppendFormat("<summary>{0}{0}</summary>", Environment.NewLine);
            }
            else
            {
                StringBuilderDocComment.Append(XmlNodeSummary.OuterXml);
            }            
            foreach (XmlNode XmlNodeChild in XmlDocumentDocComment.DocumentElement.ChildNodes)
            {
                if (XmlNodeChild.Name != "summary" && XmlNodeChild.Name != "returns" && XmlNodeChild.Name != "remarks" && XmlNodeChild.NodeType != XmlNodeType.Whitespace)
                {
                    if (XmlNodeChild.Name == "param" || XmlNodeChild.Name == "typeparam")
                    {
                        if (!WereTypeParametersDumped)
                        {
                            SyncTypeParameters(CodeElementInstance, XmlDocumentDocComment, StringBuilderDocComment);
                            WereTypeParametersDumped = true;
                        }
                        if (!WereParametersDumped)
                        {
                            SyncParameters(CodeElementInstance, XmlDocumentDocComment, StringBuilderDocComment);
                            WereParametersDumped = true;
                        }
                    }
                    else
                    {
                        StringBuilderDocComment.AppendFormat("{0}{1}", Environment.NewLine, XmlNodeChild.OuterXml);
                    }
                }
            }
            if (!WereTypeParametersDumped)
            {
                SyncTypeParameters(CodeElementInstance, XmlDocumentDocComment, StringBuilderDocComment);
            }
            if (!WereParametersDumped)
            {
                SyncParameters(CodeElementInstance, XmlDocumentDocComment, StringBuilderDocComment);
            }
            if (CodeElementInstance.HasReturnValue())
            {
                XmlNode XmlNodeReturn = XmlDocumentDocComment.DocumentElement.SelectSingleNode("returns");
                if (XmlNodeReturn == null)
                {
                    StringBuilderDocComment.AppendFormat("{0}<returns></returns>", Environment.NewLine);
                }
                else
                {
                    StringBuilderDocComment.AppendFormat("{0}{1}", Environment.NewLine, XmlNodeReturn.OuterXml);
                }
            }
            XmlNode XmlNodeRemarks = XmlDocumentDocComment.DocumentElement.SelectSingleNode("remarks");
            if (XmlNodeRemarks == null)
            {
                if (AddRemarks)
                {
                    StringBuilderDocComment.AppendFormat("{0}<remarks></remarks>", Environment.NewLine);
                }
            }
            else
            {
                StringBuilderDocComment.AppendFormat("{0}{1}", Environment.NewLine, XmlNodeRemarks.OuterXml);
            }
            CodeElementInstance.SetDocComment(Extension, StringBuilderDocComment.ToString());
        }
        private void SyncParameters(CodeElement CodeElementInstance, XmlDocument Source, StringBuilder Target)
        {
            foreach (CodeParameter Param in CodeElementInstance.GetParameters())
            {
                XmlNode XmlNodeParam = Source.SelectSingleNode(string.Format("//param[@name=\"{0}\"]", Param.Name));
                if (XmlNodeParam == null)
                {
                    Target.AppendFormat("{0}<param name=\"{1}\"></param>", Environment.NewLine, Param.Name);
                }
                else
                {
                    Target.AppendFormat("{0}{1}", Environment.NewLine, XmlNodeParam.OuterXml);
                }
            }
        }
        private void SyncTypeParameters(CodeElement CodeElementInstance, XmlDocument Source, StringBuilder Target)
        {
            foreach (string Param in CodeElementInstance.GetTypeParameters())
            {
                XmlNode XmlNodeParam = Source.SelectSingleNode(string.Format("//typeparam[@name=\"{0}\"]", Param));
                if (XmlNodeParam == null)
                {
                    Target.AppendFormat("{0}<typeparam name=\"{1}\"></typeparam>", Environment.NewLine, Param);
                }
                else
                {
                    Target.AppendFormat("{0}{1}", Environment.NewLine, XmlNodeParam.OuterXml);
                }
            }
        }
    }
}
